#include <stdio.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "ra_eventloop.h"
#include "rastate.h"

typedef struct raApiState{
    int epfd;
    struct epoll_event *events;
}raApiState;

static int ra_create_listen_sokcet(unsigned short port){
    int listen_fd = socket(PF_INET, SOCK_STREAM, 0);
    int j = 1;
    ioctl(listen_fd, FIONBIO, &j);
	setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,(const char *) &j, sizeof(j));

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(0);
    server_addr.sin_port = htons(port);

    int ret = bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (ret < 0) {
        perror("error bind");
        return ret;
    }
    ret = listen(listen_fd, 1024);
    if (ret < 0) {
        perror("error listen");
        return ret;
    }
    return listen_fd;
}

static int ra_create_ApiState(raEventLoop * eventloop){
    raApiState * estate;
    estate = malloc(sizeof(raApiState));
    if(estate == NULL) return RA_MALLOC_ERR;
    estate->events = malloc(eventloop->setsize * sizeof(struct epoll_event));
    if(estate->events == NULL){
        free(estate);
        return RA_MALLOC_ERR;
    }
    estate->epfd = epoll_create(1024);
    if(estate->epfd < 0){
        free(estate->events);
        free(estate);
        return RA_ERR;
    }
    anetCloexec(estate->epfd);
    eventloop->apidata = estate;
    return RA_OK;
}

static int ra_resize_ApiState(raEventLoop * eventloop){
    raApiState * estate = eventloop->apidata;
    struct epoll_event * events = malloc(eventloop->setsize * sizeof(struct epoll_event));
    if(events == NULL) return RA_MALLOC_ERR;
    free(estate->events);
    estate->events = events;
    return RA_OK;
}

static void ra_free_ApiState(raEventLoop *eventloop) {
    raApiState *state = eventloop->apidata;
    close(state->epfd);
    free(state->events);
    free(state);
}

static int ra_add_event_ApiState(raEventLoop *eventloop,int fd,int mask){
    raApiState  *state        = eventloop->apidata;
    raFileEvent *event        = &eventloop->events[fd];
    struct epoll_event eevent = {0};

    int op = event->mask == NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
    event->mask |= mask;

    eevent.data.fd = fd;
    if(mask & READABLE ) eevent.events |= EPOLLIN;
    if(mask & WRITEABLE) eevent.events |= EPOLLOUT;
    
    if(epoll_ctl(state->epfd,op,fd,&eevent) == -1) return RA_ERR;
    if(eventloop->maxfd < fd) eventloop->maxfd = fd;
    return RA_OK;
}

static int ra_del_event_ApiState(raEventLoop *eventloop,int fd,int delmask){
    raApiState  *state        = eventloop->apidata;
    raFileEvent *event        = &eventloop->events[fd];
    struct epoll_event eevent = {0};
    event->mask &= ~delmask;
    int op = event->mask == NONE ? EPOLL_CTL_DEL : EPOLL_CTL_MOD;
    
    eevent.data.fd = fd;
    if(event->mask & READABLE ) eevent.events |= EPOLLIN;
    if(event->mask & WRITEABLE) eevent.events |= EPOLLOUT;

    if(epoll_ctl(state->epfd,op,fd,&eevent) == -1) return RA_ERR;
    if(eventloop->maxfd < fd) eventloop->maxfd = fd;
    return RA_OK;
}

static int ra_wait(raEventLoop *eventloop, struct timeval *tvp){
    raApiState  *state;
    int         k,fd,mask;
    state = eventloop->apidata;
    k = 0;

    int nret = epoll_wait(state->epfd,state->events,eventloop->setsize,NULL);
    if(nret < 0) return RA_ERR;
    
    for (int i = 0; i < nret; i++)
    {
        fd   = state->events[i].data.fd;
        mask = 0;
        eventloop->fires[k].fd = fd;
        if(state->events[i].events & EPOLLIN ) mask |= READABLE;
        if(state->events[i].events & EPOLLOUT) mask |= WRITEABLE;
        if(state->events[i].events & EPOLLHUP) mask |= READABLE | WRITEABLE;
        if(state->events[i].events & EPOLLERR) mask |= READABLE | WRITEABLE;
        eventloop->fires[k].mask = mask;
    }
    return RA_OK;
}

static const char * ra_net_name(){
    return "epoll";
}
#if 0
int main(int argc,char *argv[]){
    char buf[1024];
    int fdserver = ra_create_listen_sokcet(6666);
    //struct epoll_wait
    struct epoll_event event;
    struct epoll_event *events = calloc(MAX_LINK_LEN,sizeof(event));

    int efd = epoll_create(MAX_LINK_LEN);
    if(efd < 0){
        perror("error epoll_create");
        return -1;
    }
    event.data.fd = fdserver;
    event.events = EPOLLIN | EPOLLET;

    if(epoll_ctl(efd,EPOLL_CTL_ADD,fdserver,&event) == -1){
        perror("error epoll_ctl");
        return -1;
    }
    while (1) {
        int n = epoll_wait(efd,events,MAX_LINK_LEN,-1);
        if(n < 0){
            perror("error epoll_wait");
            return -1;
        }

        //fdserver
        for (int i = 0; i < n; i++)
        {
            //error 
            if(events[i].events & EPOLLERR || events[i].events & EPOLLHUP){
                close(events[i].data.fd);
                if(events[i].data.fd == fdserver){
                    perror("fdserver error");
                    return -1;
                }
                perror("events error");
                continue;
            }
            //listen events
            if(events[i].data.fd == fdserver){
                struct sockaddr_in ss;
                socklen_t slen = sizeof(ss);
                int fd = accept(fdserver, (struct sockaddr *) &ss, &slen);
                if (fd < 0) {
                    perror("accept error");
                    continue;
                } else {
                    int j = 1;
                    ioctl(fd, FIONBIO, &j);
                    event.data.fd = fd;
                    event.events = EPOLLIN ;//| EPOLLET;
                    if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event) == -1) {
                        perror("epoll_ctl add error");
                        close(fd);
                        continue;
                    }
                }
                continue;
            }
            //read
            if(events[i].events & EPOLLIN){
                int fd = events[i].data.fd;
                int ret = recv(fd,buf,1024,0);
                if(ret > 0){
                    dataLen = ret;
                    printf("data:%.*s\n",ret,buf);
                    //events[i].data.fd = fd;
                    events[i].events = EPOLLOUT;
                    if (epoll_ctl(efd,EPOLL_CTL_MOD,fd,&events[i]) == -1) {
                        perror("ret == 0 epoll_ctl mod error");
                        close(fd);
                    }
                    continue;
                }else if(ret == 0){
                    if (epoll_ctl(efd,EPOLL_CTL_DEL,fd,&events[i]) == -1) {
                        perror("ret == 0 epoll_ctl del error");
                    }
                    close(fd);
                    printf("close connection fd = %d\n",fd);
                }else{
                    if (ret == EWOULDBLOCK || ret == EINPROGRESS){
                        continue;
                    }else{
                        if (epoll_ctl(efd,EPOLL_CTL_DEL,fd,&event)== -1) {
                            perror("epoll_ctl del error");
                        }
                        close(fd);
                        printf("ret < 0 close connection fd = %d",fd);
                    }
                }
                continue;
            }

            //write
            if(events[i].events & EPOLLOUT){
                int fd = events[i].data.fd;
                int ret = send(fd,buf,dataLen,0);
                if (ret < 0 ){
                    if (ret == EWOULDBLOCK || ret == EINPROGRESS){
                        continue;
                    }
                }
                events[i].events = EPOLLIN;
                if (epoll_ctl(efd,EPOLL_CTL_MOD,fd,&events[i]) == -1) {
                        perror("ret == 0 epoll_ctl mod error");
                        close(fd);
                }
                continue;
            }
        }
    }
    return 0;
}
#endif